package session

import (
	"time"

	"git.oschina.net/blueszhang/stock_be/gmap"

	"code.google.com/p/go-uuid/uuid"
	log "github.com/Sirupsen/logrus"
)

var (
	sessionToSmsCode *gmap.Map //gmap.Map is thread-safety
	currBucket       int
)

func init() {
	sessionToSmsCode = gmap.NewMap(16)
	currBucket = 0
	go clearExpiredItems()
}

type Session struct {
	Id      string    `json:"id"`
	Expires time.Time `json:"expires"`
	items   map[string]interface{}
}

func NewSession() *Session {
	sid := getSid()
	s := &Session{
		Id:      sid,
		Expires: time.Now().Add(time.Minute * 2),
		items:   make(map[string]interface{}),
	}
	sessionToSmsCode.Set(sid, s)
	return s
}

func clearExpiredItems() {
	interval := time.NewTicker(1 * time.Minute)
	select {
	case <-interval.C:
		now := time.Now()
		for currBucket = 0; currBucket < 16; currBucket++ {
			bucketLock := sessionToSmsCode.BucketLock(currBucket)
			bucketLock.Lock()
			bucket := sessionToSmsCode.Bucket(currBucket)
			for k, v := range bucket {
				session := v.(*Session)
				if now.After(session.Expires) {
					delete(bucket, k)
				}
			}
			bucketLock.Unlock()
		}
	}
}

func (s *Session) Put(key string, value interface{}) {
	s.items[key] = value
}

func (s *Session) Get(key string) (interface{}, bool) {
	val, ok := s.items[key]
	return val, ok
}

func Get(sid string) (*Session, bool) {
	itr, ok := sessionToSmsCode.Get(sid)
	if !ok {
		return nil, false
	}
	session, ok := itr.(*Session)
	if !ok {
		log.Fatalln("Cannot cast to session")
	}
	if time.Now().After(session.Expires) {
		sessionToSmsCode.Delete(sid)
		return nil, false
	}
	return session, true
}

func getSid() string {
	sid := uuid.New()
	return sid
}
